#define _CRT_SECURE_NO_WARNINGS

#include <cmath>
#include <cstring>
#include <map>

#include "config.h"
#include "file_op.h"
#include "str_op.h"

#include "network.h"

// Extern variables.
std::vector<int> *edges;
size_t num_of_compounds;
size_t num_of_nodes;
std::vector<double> *compound_Ki_list;
std::vector<int> vertex_exit;

static std::map<std::string, int> vertex_map;

void load_vertex()
{
    //Load the kPi value of all the known nodes.
    FILE *kpi_file = open_file_read(pki_file_path, "cannot open kpi file.");
    char buffer[LINE_BUFFER_SIZE];
    //Read the title line.
    if(fgets(buffer, LINE_BUFFER_SIZE, kpi_file) == NULL)
    {
        printf("Failed to read the title line of the kpi file.");
        exit(-1);
    }
    //Find how many comma in the line.
    size_t line_length = strlen(buffer);
    int last_start = 0, columns = 0;
    while(last_start < line_length)
    {
        // Increase column.
        ++columns;
        //Find the next comma.
        int comma_pos = indexOf(',', buffer, line_length, last_start);
        //If cannot find comma, then end search.
        if(comma_pos == -1)
        {
            break;
        }
        //Mark the last start search position.
        last_start = comma_pos + 1;
    }
    //Check columns of the row.
    if(columns <= 2)
    {
        //No compound is found.
        printf("No compound pKi is provided.");
        exit(-1);
    }
    //First column is the name of the node, the second column is the exit mark,
    //Hence, number of compounds is columns minus 2.
    num_of_compounds = static_cast<size_t>(columns - 2);
    //Create Ki lists.
    compound_Ki_list = new std::vector<double>[num_of_compounds];
    //Create the buffer.
    int *comma_pos = new int[num_of_compounds];
    double pki;
    //Read all the node information.
    while(fgets(buffer, LINE_BUFFER_SIZE, kpi_file) != NULL)
    {
        //Process the buffer, find the first comma.
        size_t line_length = strlen(buffer);
        int first_comma = indexOf(',', buffer, line_length);
        if(first_comma == -1)
        {
            printf("Cannot find node name for line:\n%s\n", buffer);
            exit(-1);
        }
        // It should contain the first comma.
        std::string node_name(buffer, static_cast<size_t>(first_comma));
        //Insert to map.
        int node_index = compound_Ki_list[0].size();
        vertex_map.insert(std::pair<std::string, int>(node_name, node_index));
        // The second comma should be exist as well.
        int last_comma = indexOf(',', buffer, line_length, first_comma + 1);
        if(last_comma == -1)
        {
            //Okay, we allows no marking for the exit, but which means this is the last column.
            continue;
        }
        // Check the whether the exit part is not empty.
        if(last_comma != first_comma+1)
        {
            // Add the node index to exit node.
            vertex_exit.push_back(node_index);
        }
        // Reset and read all the pKi value.
        bool skip_mark = false;
        for(int i=0; i<num_of_compounds; ++i)
        {
            //For the first one, we does not need to search.
            if(i==0)
            {
                comma_pos[i] = last_comma;
                continue;
            }
            //Search from last comma.
            comma_pos[i] = indexOf(',', buffer, line_length, last_comma + 1);
            //Failed to find comma.
            if(comma_pos[i] == -1)
            {
                printf("Failed to find enough comma in line:\n%s\n", buffer);
                exit(-1);
            }
            // Check whether the column is empty.
            if(comma_pos[i] == last_comma + 1)
            {
                skip_mark=true;
                break;
            }
            last_comma = comma_pos[i];
        }
        // Check whether comma is empty.
        if(skip_mark)
        {
            printf("%s has no pKi\n", node_name.c_str());
            for(size_t i=0; i<num_of_compounds; ++i)
            {
                compound_Ki_list[i].push_back(-1);
            }
            continue;
        }
        // Parse the data.
        for(size_t i=0; i<num_of_compounds; ++i)
        {
            //Read the data.
            if(sscanf(buffer + comma_pos[i] + 1, "%lf", &pki) == -1)
            {
                //Failed to read the data, ignore the line.
                if(num_of_compounds > 1)
                {
                    printf("%s has an unreadable compound pKi.\n", node_name.c_str());
                    exit(-1);
                }
                else
                {
                    printf("%s has no pKi\n", node_name.c_str());
                }
                compound_Ki_list[i].push_back(-1);
                continue;
            }
            //Convert pKi to Ki and save to buffer:
            //  Ki = 10 ^ (-pKi)
            compound_Ki_list[i].push_back(pow(10, -pki));
        }
    }
    fclose(kpi_file);
    // Clear the buffer.
    delete[] comma_pos;
    // Get the number of nodes.
    num_of_nodes = compound_Ki_list[0].size();
}

void load_edge()
{
    //Allocate vertex buffer.
    edges = new std::vector<int>[num_of_nodes];
    //Load the network connections.
    FILE *network_file = open_file_read(network_file_path, "cannot open network file.");
    char buffer[LINE_BUFFER_SIZE];
    while(fgets(buffer, LINE_BUFFER_SIZE, network_file) != NULL)
    {
        //Find the first comma,
        size_t line_length = strlen(buffer);
        int first_comma = indexOf(',', buffer, line_length);
        if(first_comma == -1)
        {
            //Ignore the current line.
            continue;
        }
        //Get the node start.
        std::string node_start(buffer, static_cast<size_t>(first_comma)), node_end;
        //Maybe we have the second comma, try to find the second one.
        int second_comma = indexOf(',', buffer, line_length, first_comma+1);
        if(second_comma == -1)
        {
            // Well, it is okay, all the string left would be the node end.
            node_end = std::string(buffer+first_comma+1, line_length-first_comma-2);
        }
        else
        {
            // Fine, set as all the string left.
            node_end = std::string(buffer+first_comma+1,
                                   static_cast<size_t>(second_comma-first_comma-1));
        }
        // Get the node start and end id.
        edges[vertex_map.at(node_start)].push_back(vertex_map.at(node_end));
    }
    fclose(network_file);

}

void free_network()
{
    delete[] edges;
    delete[] compound_Ki_list;
}
